#include "HDF5DataSetObject.h"

///
///Split a string str by delimiter and save the substrings into tokens.
///
void Tokenize(const H5std_string& str, vector<H5std_string>& tokens, const H5std_string& delimiters)
{
	H5std_string _str(str);

	H5std_string::iterator It=_str.begin();
	// Skip delimiters at beginning.
	H5std_string::size_type lastPos = _str.find_first_not_of(delimiters, 0);
	// Find first "non-delimiter".
	H5std_string::size_type pos     = _str.find_first_of(delimiters, lastPos);

	while (H5std_string::npos != pos || H5std_string::npos != lastPos)
	{
		// Found a token, add it to the vector.
		tokens.push_back(_str.substr(lastPos, pos - lastPos));
		// Skip delimiters.  Note the "not_of"
		lastPos = _str.find_first_not_of(delimiters, pos);
		// Find next "non-delimiter"
		pos = _str.find_first_of(delimiters, lastPos);
	}
}


CHDF5DataSetObject::CHDF5DataSetObject(int Rank, const hsize_t *Dims,DataType& dataType)
:rank(Rank),dimensions(Dims),dType(dataType),size(1)
{
	for (int i=0;i<Rank;i++)
	{
		size=size*(int)Dims[i];
	}
}

CHDF5DataSetObject::~CHDF5DataSetObject()
{
}

int CHDF5DataSetObject::write(H5File* h5_file, const H5std_string& location, void* data)
{
	int res=0;

	hsize_t* sizet=new hsize_t[rank];
	hsize_t* offset=new hsize_t[rank];
	buf=data;

	try
	{
		Exception::dontPrint();

		createDataSet(h5_file,location);

		DataSet dataset=h5_file->openDataSet(location);

		for (int i=0;i<rank;i++)
		{
			offset[i]=0;
		}
		dataset.extend(dimensions);

		DataSpace fileSpace = dataset.getSpace();

		fileSpace.selectHyperslab(H5S_SELECT_SET,dimensions,offset);

		dataset.write(buf,dataset.getDataType());

		dataset.close();


		res=1;
	}
	// catch failure caused by the H5File operations
	catch( FileIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSet operations
	catch( DataSetIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataSpaceIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataTypeIException error )
	{
		errMsg=error.getDetailMsg();
	}
	catch( AttributeIException error )
	{
		errMsg=error.getDetailMsg();
	}

	delete[] sizet;
	delete[] offset;

	return res;
}
int CHDF5DataSetObject::append(H5File* h5_file, const H5std_string& location, void* data)
{
	int res=0;

	hsize_t* sizet=new hsize_t[rank];
	hsize_t* offset=new hsize_t[rank];
	buf=data;

	try
	{
		Exception::dontPrint();

		if (isDataSetExist(h5_file,location))
		{
			
			DataSet dataset=h5_file->openDataSet(location);

			DataSpace ds=dataset.getSpace();

			hsize_t* dims_out=new hsize_t[rank];
			int ndims=ds.getSimpleExtentDims(dims_out);
			for (int i=0;i<rank;i++)
			{
				int t=dims_out[i];
				sizet[i]=dims_out[i]+dimensions[i];
				t=sizet[i];
				offset[i]=dims_out[i];
				
			}
			dataset.extend( sizet );

			DataSpace fileSpace = dataset.getSpace ();
			fileSpace.selectHyperslab( H5S_SELECT_SET, dimensions, offset );

			DataSpace mspace(rank,dimensions);
			dataset.write(buf,dataset.getDataType(),mspace,fileSpace);

			//int 
			//Attribute rowAttr=dataset.openAttribute(ROW_NUM);
			//int rsize;
			//rowAttr.read(attrDType,&rsize);
			//rsize+=(int)dimensions[0];
			//rowAttr.write(attrDType,&rsize);

			//Attribute colAttr=dataset.openAttribute(COL_NUM);
			//int csize;
			//colAttr.read(attrDType,&csize);
			//csize+=(int)dimensions[1];
			//colAttr.write(attrDType,&csize);

			dataset.close();
			delete dims_out;
		}

	}
	// catch failure caused by the H5File operations
	catch( FileIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSet operations
	catch( DataSetIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataSpaceIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataTypeIException error )
	{
		errMsg=error.getDetailMsg();
	}
	catch( AttributeIException error )
	{
		errMsg=error.getDetailMsg();
	}

	delete[] sizet;
	delete[] offset;

	return res;
}
int CHDF5DataSetObject::read(H5File* h5_file,const H5std_string& location,void* dataOut)
{
	int res=0;

	try
	{
		Exception::dontPrint();

		if (isFileExist(h5_file->getFileName())&&isDataSetExist(h5_file,location))
		{	
			DataSet dataset=h5_file->openDataSet(location);
			DataSpace ds=dataset.getSpace();

			dataset.read(dataOut,dataset.getDataType(),ds,ds);
		}

	}
	// catch failure caused by the H5File operations
	catch( FileIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSet operations
	catch( DataSetIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataSpaceIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataTypeIException error )
	{
		errMsg=error.getDetailMsg();
	}

	return res;
}

int CHDF5DataSetObject::createAttribute(H5File* h5_file,const H5std_string& dataSetName,H5std_string& attrName,DataType& attrType,const void* data)
{
	int res=0;
	try
	{
		Exception::dontPrint();

		DataSpace attr_dataspace = DataSpace(H5S_SCALAR);

		DataSet dataSet=h5_file->openDataSet(dataSetName);
		Attribute attr=dataSet.createAttribute(attrName,attrType,attr_dataspace);
		attr.write(attrType,data);
		res=1;
	}
	// catch failure caused by the H5File operations
	catch( FileIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSet operations
	catch( DataSetIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataSpaceIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataTypeIException error )
	{
		errMsg=error.getDetailMsg();
	}
	catch( AttributeIException error )
	{
		errMsg=error.getDetailMsg();
	}
	return res;
}
int CHDF5DataSetObject::readAttribute(H5File* h5_file,const H5std_string& dataSetName,H5std_string& attrName,DataType& attrType,void* data)
{
	int res=0;
	try
	{
		Exception::dontPrint();

		DataSet dataSet=h5_file->openDataSet(dataSetName);
		Attribute attr=dataSet.openAttribute(attrName);
		attr.read(attrType,data);
		res=1;
	}
	// catch failure caused by the H5File operations
	catch( FileIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSet operations
	catch( DataSetIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataSpaceIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataTypeIException error )
	{
		errMsg=error.getDetailMsg();
	}
	catch( AttributeIException error )
	{
		errMsg=error.getDetailMsg();
	}
	return res;
}

int CHDF5DataSetObject::modifyAttribute(H5File* h5_file,const H5std_string& dataSetName,H5std_string& attrName,DataType& attrType,const void* data)
{
	int res=0;
	try
	{
		Exception::dontPrint();

		DataSet dataSet=h5_file->openDataSet(dataSetName);
		Attribute attr=dataSet.openAttribute(attrName);
		attr.write(attrType,data);
		res=1;
	}
	// catch failure caused by the H5File operations
	catch( FileIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSet operations
	catch( DataSetIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataSpaceIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataTypeIException error )
	{
		errMsg=error.getDetailMsg();
	}
	catch( AttributeIException error )
	{
		errMsg=error.getDetailMsg();
	}
	return res;
}

void CHDF5DataSetObject::createDataSet(H5File* h5_file,const H5std_string& location)
{
	hsize_t* maxdims=new hsize_t[rank];
	hsize_t* chunk_dims=new hsize_t[rank];

	try
	{
		Exception::dontPrint();

		for (int i=0;i<rank;i++)
		{
			maxdims[i]=H5S_UNLIMITED;
			chunk_dims[i]=dimensions[i];
		}

		DataSpace dataspace(rank,dimensions,maxdims);

		DSetCreatPropList cparms;

		cparms.setChunk(rank,chunk_dims);

		//int fill_val = 0;
		//cparms.setFillValue( dataType, &fill_val);

		vector<H5std_string> paths;
		Tokenize(location,paths,"/");
		createGroups(h5_file,paths);

		h5_file->createDataSet(location,dType,dataspace,cparms);
	}
	// catch failure caused by the H5File operations
	catch( FileIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSet operations
	catch( DataSetIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataSpaceIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataTypeIException error )
	{
		errMsg=error.getDetailMsg();
	}
	delete[] maxdims;
	delete[] chunk_dims;
}

void CHDF5DataSetObject::createGroups(H5File* h5_file,vector<H5std_string>& paths)
{
	try
	{
		Exception::dontPrint();
		H5std_string groupName="";
		for (vector<H5std_string>::size_type i=0;i<paths.size()-1;i++)
		{
			groupName=groupName+"/"+paths[i];
			if (!isGroupExist(h5_file,groupName))
			{
				Group group(h5_file->createGroup(groupName));
			}
			else
			{
				break;
			}

		}
	}
	// catch failure caused by the H5File operations
	catch( FileIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the Group operations
	catch( GroupIException error )
	{
		errMsg=error.getDetailMsg();
	}

}
int CHDF5DataSetObject::getSize(H5File* h5_file,const H5std_string& location)
{
	int size=1;
	try
	{
		Exception::dontPrint();

		if (isFileExist(h5_file->getFileName())&&isDataSetExist(h5_file,location))
		{	
			DataSet dataset=h5_file->openDataSet(location);
			DataSpace ds=dataset.getSpace();
			int rank=ds.getSimpleExtentNdims();
			hsize_t* dims_out=new hsize_t[rank];
			int ndims=ds.getSimpleExtentDims(dims_out);
			for (int i=0;i<rank;i++)
			{
				size=size*(int)dims_out[i];
			}

		}


	}
	// catch failure caused by the H5File operations
	catch( FileIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSet operations
	catch( DataSetIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataSpaceIException error )
	{
		errMsg=error.getDetailMsg();
	}

	// catch failure caused by the DataSpace operations
	catch( DataTypeIException error )
	{
		errMsg=error.getDetailMsg();
	}
	catch( AttributeIException error )
	{
		errMsg=error.getDetailMsg();
	}
	return size;

}
bool CHDF5DataSetObject::isFileExist(const H5std_string& filename)
{
	struct stat stFileInfo;
	bool blnReturn;
	int intStat;

	// Attempt to get the file attributes
	intStat = stat(filename.c_str(),&stFileInfo);
	if(intStat == 0) {
		// We were able to get the file attributes
		// so the file obviously exists.
		blnReturn = true;
	} else {
		// We were not able to get the file attributes.
		// This may mean that we don't have permission to
		// access the folder which contains this file. If you
		// need to do that level of checking, lookup the
		// return values of stat which will give you
		// more details on why stat failed.
		blnReturn = false;
	}

	return(blnReturn); 
}
bool CHDF5DataSetObject::isDataSetExist(H5File* h5_file,const H5std_string& location)
{

	try
	{
		Exception::dontPrint();
		h5_file->openDataSet(location);
	}
	// catch failure caused by the H5File operations
	catch( FileIException error )
	{
		return false;
	}
	return true;
}

bool CHDF5DataSetObject::isGroupExist(H5File* h5_file,const H5std_string& location)
{

	try
	{
		Exception::dontPrint();
		h5_file->openGroup(location);
	}
	// catch failure caused by the H5File operations
	catch( FileIException error )
	{
		return false;
	}
	return true;
}

H5std_string CHDF5DataSetObject::GetErrMsg()const
{
	return errMsg;
}